#pragma once
#include <iostream>
#include <vector>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <functional>
#include <future>
#include <memory>
using namespace std;

class ThreadPool
{
    using func_t = function<void(void)>; // 统一不同函数的类型

public:
    ThreadPool(int thread_num = 3)
        : _stop(false)
    {
        for (int i = 0; i < thread_num; i++)
        { // 原地构造, 不会发生拷贝
            _threads.emplace_back(&ThreadPool::Run, this);
        }
    }
    ~ThreadPool()
    {
        Stop();
    }
    void Stop()
    {
        if (_stop)
            return;
        _stop = true;
        _cond.notify_all();
        for (auto &t : _threads)
        {
            t.join();
        }
    }

    // 传入的参数是：函数+不定参数
    template <typename F, typename... Args>
    auto Push(F &&func, Args &&...args) -> future<decltype(func(args...))> // 类型推导
    {
        // 1.封装packaged_task任务
        using ret_type = decltype(func(args...)); // 获取函数返回值类型
        // 函数绑定，后期任务队列函数执行的时候不用传参数
        auto f = bind(forward<F>(func), forward<Args>(args)...); // 完美转发，类型保持
        // 设置成智能指针的目的是延长生命周期
        // 设置进去的函数是bind之后的
        auto pt = make_shared<packaged_task<ret_type(void)>>(f);
        future<ret_type> fut = pt->get_future();

        // 2.构造匿名任务函数
        {
            unique_lock<mutex> lck(_mtx);
            // 设置成lambda表达式后并不会立即执行,只是类似于设置一个方法函数进去
            _tasks.push([pt]()
                        { (*pt)(); });

            _cond.notify_one();
            return fut;
        }
    }

private:
    void Run()
    {
        while (!_stop)
        {
            func_t task;
            {
                unique_lock<mutex> lck(_mtx);
                // 当任务队列有任务时，或者终止线程(满足一个就停止等待，前面满足后面的不判断)
                _cond.wait(lck, [this]()
                           { return !this->_tasks.empty() || this->_stop; });
                if (_stop) // 防止还继续拿任务执行
                    break;
                task = _tasks.front();
                _tasks.pop();
            }
            // 任务执行
            task();
        }
        cout << "i am going " << this_thread::get_id() << endl;
    }

private:
    atomic<bool> _stop;
    mutex _mtx;
    condition_variable _cond;
    queue<func_t> _tasks;
    vector<thread> _threads;
};